﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace personalplaner.business.extensions
{
	public static class ObjectExtensions
	{
        /// <summary>
        /// Creates a new Instance of T and copies all public properties form the original to the new
        /// created object. referenz properties are not supported.
        /// </summary>
        /// <typeparam name="T">The type to be used for copy the object</typeparam>
        /// <param name="objFrom">Object to be cloned.</param>
        /// <returns>New instanzce of this object.</returns>
        public static T Clone<T>(this T objFrom) where T : class,new()
        {
            if (objFrom == null)
                return null;
            T newItem = new T();

            Type typeFrom = objFrom.GetType();
            Type typeTo = newItem.GetType();
            List<PropertyInfo> listPropertyInfo = new List<PropertyInfo>();

            foreach (PropertyInfo propertyInfo in typeTo.GetProperties())
            {
                if (propertyInfo.Name == "IsNew" || propertyInfo.Name == "IsNull")
                    continue;

                if (!((propertyInfo.CanRead == true) && (propertyInfo.CanWrite == true) && propertyInfo.GetIndexParameters().Length == 0))
                    continue;
                listPropertyInfo.Add(propertyInfo);
            }

            foreach (PropertyInfo propertyInfo in typeFrom.GetProperties())
            {
                if (propertyInfo.Name == "IsNew" || propertyInfo.Name == "IsNull")
                    continue;

                if (!((propertyInfo.CanRead == true) && (propertyInfo.CanWrite == true) && propertyInfo.GetIndexParameters().Length == 0))
                    continue;

                object tvalue = propertyInfo.GetValue(objFrom, null);
                PropertyInfo piClone = (from pi in listPropertyInfo
                                        where pi.Name == propertyInfo.Name
                                        select pi).FirstOrDefault();

                piClone.SetValue(newItem, tvalue, null);
            }

            return newItem;
        }

		/// <summary>
		/// Copies all properties from one object to the other.
		/// Referenz properties are not supported.
		/// </summary>
		/// <typeparam name="T">The type to be used for copy the object.</typeparam>
		/// <param name="objFrom">Source object to copy</param>
		/// <param name="objTo">Target object</param>
		/// <returns>True, if successful copied otherwise false.</returns>
		public static bool CopyTo<T>(this object objFrom, object objTo) where T : class,new()
		{

			Type typeFrom = objFrom.GetType();
			Type typeTo = objTo.GetType();

			if (typeFrom.Name != typeTo.Name)
			{
				System.Diagnostics.Debug.Assert(false);
				return false;
			}

			List<PropertyInfo> listPropertyInfo = new List<PropertyInfo>();
			foreach (PropertyInfo propertyInfo in typeTo.GetProperties())
			{
				if (!((propertyInfo.CanRead == true) && (propertyInfo.CanWrite == true)))
					continue;
				listPropertyInfo.Add(propertyInfo);
			}

			foreach (PropertyInfo propertyInfo in typeFrom.GetProperties())
			{
				if (!((propertyInfo.CanRead == true) && (propertyInfo.CanWrite == true)))
					continue;
				object tvalue = propertyInfo.GetValue(objFrom, null);

				PropertyInfo piClone = (from pi in listPropertyInfo
										where pi.Name == propertyInfo.Name
										select pi).FirstOrDefault();

				piClone.SetValue(objTo, tvalue, null);
			}
			return true;
		}


		/// <summary>
		/// Compares the properties of the objects and returs true if they are equal.
		/// referenz properties are not supported.
		/// </summary>
		/// <typeparam name="T">The type to be used for comparing.</typeparam>
		/// <param name="objOriginal">this object</param>
		/// <param name="objCopy">Object to compare</param>
		/// <returns>True, if equal otherwise false.</returns>
		public static bool IsEqual<T>(this object objOriginal, object objCopy) where T : class
		{
			if (objCopy == null)
				return false;

			Type typeOriginal = objOriginal.GetType();
			Type typeCopy = objCopy.GetType();

			if (typeOriginal.Name != typeCopy.Name)
				return false;

			List<PropertyInfo> listPropertyInfo = new List<PropertyInfo>();
			foreach (PropertyInfo propertyInfo in typeCopy.GetProperties())
			{
				if (!((propertyInfo.CanRead == true) && (propertyInfo.CanWrite == true)))
					continue;
				listPropertyInfo.Add(propertyInfo);
			}

			foreach (PropertyInfo propertyInfo in typeOriginal.GetProperties())
			{
				if (!((propertyInfo.CanRead == true) && (propertyInfo.CanWrite == true)))
					continue;
				object valueOriginal = propertyInfo.GetValue(objOriginal, null);

				PropertyInfo piCopy = (from pi in listPropertyInfo
									   where pi.Name == propertyInfo.Name
									   select pi).FirstOrDefault();

				object valueCopy = piCopy.GetValue(objCopy, null);

				if ((valueOriginal == null) || (valueCopy == null))
				{
					if (valueOriginal != valueCopy)
					{
						string sValueOriginal = "NULL";
						string sValueCopy = "NULL";
						if (valueOriginal != null)
							sValueOriginal = valueOriginal.ToString();
						if (valueCopy != null)
							sValueCopy = valueCopy.ToString();

						Trace.WriteLine("!ObjectExt.IsEqual " + typeOriginal.Name + "." + propertyInfo.Name + " (" + sValueOriginal + "/" + sValueCopy + ")");
						return false;
					}
				}
				else if (!valueOriginal.Equals(valueCopy))
				{
					Trace.WriteLine("!ObjectExt.IsEqual " + typeOriginal.Name + "." + propertyInfo.Name + " (" + valueOriginal.ToString() + "/" + valueCopy.ToString() + ")");
					return false;
				}
			}

			return true;
		}
	}
}
